use std::io;
use std::net::{Ipv4Addr, Ipv6Addr, UdpSocket};
use std::str::from_utf8;
use std::time::Duration;

fn main() -> io::Result<()> {
    {
        // 从给定的地址创建一个 UDP 套接字。
        let socket = UdpSocket::bind("127.0.0.1:7788").expect("couldn't bind to address");

        // 返回创建此套接字的套接字地址。
        println!("{:?}", socket.local_addr());
        // 返回此套接字的远程地址。
        println!("{:?}", socket.peer_addr());

        // 将此 UDP 套接字移入或移出非阻塞模式。
        // socket.set_nonblocking(true).expect("set_nonblocking call failed");


        // 为底层套接字创建一个新的独立的拥有所有权的句柄。
        // 返回的 UdpSocket 是与此对象引用相同的套接字的引用。 两个句柄都将读取和写入相同的端口，并且在一个套接字上设置的选项将传播到另一个套接字。
        let _socket2 = socket.try_clone().expect("couldn't clone socket");

        //获取此套接字上 SO_ERROR 选项的值。
        // 这将检索底层套接字中存储的错误，从而清除进程中的字段。 这对于检查两次调用之间的错误很有用。
        // println!("{:?}", socket.take_error().unwrap());
        match socket.take_error() {
            Ok(Some(error)) => println!("UdpSocket error: {error:?}"),
            Ok(None) => println!("No error"),
            Err(error) => println!("UdpSocket.take_error failed: {error:?}"),
        }

        // 向给定的地址发送单个数据报消息。
        // socket.send_to(&[1, 2, 3], "127.0.0.1:7788")?;

        // 将读取超时设置为指定的超时。
        // socket.set_read_timeout(Some(Duration::from_secs(5)))?;

        // 将写超时设置为指定的超时。
        // socket.set_write_timeout(Some(Duration::from_secs(5)))?;
        // socket.set_write_timeout(None).expect("set_write_timeout call failed");

        // 返回此套接字的读取超时。
        println!("{:?}", socket.read_timeout());

        // 返回此套接字的写超时。
        println!("{:?}", socket.write_timeout());

        // 设置此套接字上 IP_TTL 选项的值。
        // socket.set_ttl(127)?;
        socket.set_ttl(42).expect("set_ttl call failed");

        // 获取此套接字的 IP_TTL 选项的值。 此值设置从该套接字发送的每个数据包中使用的生存时间字段。
        println!("{}", socket.ttl().unwrap());

        // 设置此套接字的 SO_BROADCAST 选项的值。
        // socket.set_broadcast(true)?;
        // socket.set_broadcast(false).expect("set_broadcast call failed");

        // 获取此套接字的 SO_BROADCAST 选项的值。
        println!("{}", socket.broadcast().unwrap());

        // 设置此套接字的 IP_MULTICAST_LOOP 选项的值。
        // 如果启用，则多播数据包将循环回到本地套接字。 请注意，这可能对 IPv6 套接字没有任何影响。
        // socket.set_multicast_loop_v4(true)?;

        // 获取此套接字的 IP_MULTICAST_LOOP 选项的值。
        println!("IP_MULTICAST_LOOP = {}", socket.multicast_loop_v4().unwrap());

        // 设置此套接字的 IP_MULTICAST_TTL 选项的值。
        // 指示此套接字的传出多播数据包的生存时间值。 默认值为 1，这意味着除非明确请求，否则多播数据包不会离开本地网络。
        // socket.set_multicast_ttl_v4(42).expect("set_multicast_ttl_v4 call failed");


        // 返回此套接字连接到的远程对等方的套接字地址。
        // println!("peer_addr = {:?}", socket.peer_addr());

        // 返回此套接字连接到的本地套接字地址。
        println!("local_addr = {:?}", socket.local_addr());


        // // 此套接字指定一个新的多播组。 该地址必须是有效的多播地址，而 interface 是系统应加入多播组的本地接口的地址。
        // // 如果等于 INADDR_ANY，则系统选择适当的接口。
        // socket.join_multicast_v4(&Ipv4Addr::new(224, 0, 0, 251), &Ipv4Addr::new(127, 0, 0, 1))?;
        // // 此函数从此套接字中删除一个多播组。 该地址必须是有效的多播地址，而 interface 是系统应加入多播组的本地接口的地址。
        // socket.leave_multicast_v4(&Ipv4Addr::new(224, 0, 0, 251), &Ipv4Addr::new(127, 0, 0, 1))?;
        //
        // // 此函数为此套接字指定一个新的多播组。 该地址必须是有效的多播地址，并且 interface 是接口到 join/leave 的索引 (或 0 表示任何接口)。
        // socket.join_multicast_v6(&Ipv6Addr::new(0xff02, 0, 0, 0, 0, 0, 0, 0x0000000000000001), 0)?;
        // // 此函数从此套接字中删除一个多播组。 该地址必须是有效的多播地址，并且 interface 是接口到 join/leave 的索引 (或 0 表示任何接口)。
        // socket.leave_multicast_v6(&Ipv6Addr::new(0xff02, 0, 0, 0, 0, 0, 0, 0x0000000000000001), 0)?;


        loop {
            let mut buf = [0; 1024];
            //-------------------------------------- 接收数据 ---------------------------------------------
            // match socket.recv(&mut buf) {
            //     Ok(received) => println!("received {received} bytes {:?}", &buf[..received]),
            //     Err(e) => println!("recv function failed: {e:?}"),
            // }

            // 在套接字上接收单个数据报消息。 成功时，返回读取的字节数和源。必须使用足够大的有效字节数组 buf 来调用函数，以容纳消息字节。
            // 如果消息太长而无法容纳在提供的缓冲区中，则多余的字节可能会被丢弃。
            // let (number_of_bytes, src_addr) = socket.recv_from(&mut buf)?;
            let (number_of_bytes, src_addr) = socket.recv_from(&mut buf).expect("Didn't receive data");
            println!("received {} bytes from {}, get message {:?}", number_of_bytes, src_addr, from_utf8(&buf[..number_of_bytes]));

            // 在套接字上接收单个数据报消息，而无需将其从队列中删除。 成功时，返回读取的字节数和源。
            // 必须使用足够大的有效字节数组 buf 来调用函数，以容纳消息字节。 如果消息太长而无法容纳在提供的缓冲区中，则多余的字节可能会被丢弃。
            // let (number_of_bytes, src_addr) = socket.peek_from(&mut buf).expect("Didn't receive data");
            let filled_buf = &mut buf[..number_of_bytes];

            let msg = from_utf8(filled_buf).expect("Didn't receive data");

            // let last_name = String::from(" === return ");
            // let return_msg = last_name + &msg;
            let return_msg = format!("return == msg {}", msg);


            //-------------------------------------- 发送数据 ---------------------------------------------
            // 将 `buf` 声明为接收数据的切片，并将反向数据发送回原点。
            // let buf = &mut buf[..number_of_bytes];
            // 反转 `buf` 中的字节。
            // filled_buf.reverse();

            // 将套接字上的数据发送到它所连接的远程地址。
            // socket.send(&[0, 1, 2]).expect("couldn't send message");
            // 将套接字上的数据发送到给定的地址。成功时，返回写入的字节数。
            socket.send_to(return_msg.as_bytes(), &src_addr).expect("couldn't send message");
        }

        println!("Hello, world!");
    }
    Ok(())
}

